#include "FLOW_core/flow_core_pch.h"
#include "FLOW_core/StrIdMgr.h"

//! Number of buckets that the CStrIdMgr will have
#define STRIDMGR_MIN_BUCKETS	1024

namespace CORE
{
	//=========================================================================

	CStrIdMgr* CStrIdMgr::sg_p_strid_mgr = NULL;

	//=========================================================================

	CStrIdMgr::TStringHolder::TStringHolder( )
		: m_psz_s( NULL )
	{
	}

	//=========================================================================

	CStrIdMgr::TStringHolder::~TStringHolder( )
	{
		SAFE_DELETE( m_psz_s );
	}

	//=========================================================================

	void CStrIdMgr::TStringHolder::Set( const char_t* apsz_s )
	{
		if( !SzCheck( apsz_s ) )
			return;

		SAFE_DELETE( m_psz_s );
		m_psz_s = SzCopy( apsz_s );
	}

	//=========================================================================

	bool_t CStrIdMgr::TStringHolder::operator == ( const CStrIdMgr::TStringHolder& ar_str_holder )
	{
		return this->Equals( ar_str_holder );
	}

	//=========================================================================

	bool_t CStrIdMgr::TStringHolder::Equals( const CStrIdMgr::TStringHolder& ar_str_holder )
	{
		return ( strcmp( m_psz_s, ar_str_holder.m_psz_s ) == 0 );
	}

	//=========================================================================

	bool_t CStrIdMgr::TStringHolder::Equals( const char_t* ap_sz )
	{
		return ( strcmp( m_psz_s, ap_sz ) == 0 );
	}

	//=========================================================================

	const char_t* CStrIdMgr::TStringHolder::c_str( void )
	{
		return m_psz_s;
	}

	//=========================================================================

	CStrIdMgr::CStrIdMgr( )
		: m_hm_strids( STRIDMGR_MIN_BUCKETS )
	{
	}

	//=========================================================================

	CStrIdMgr::~CStrIdMgr( )
	{
		TSzHashMap::iterator it = m_hm_strids.begin( );
		TSzHashMap::iterator end = m_hm_strids.end( );

		for( ; it != end; ++it )
			SAFE_DELETE( it->second );
	}

	//=========================================================================

	CStrIdMgr* CStrIdMgr::GetInstance( void )
	{
		if( CStrIdMgr::sg_p_strid_mgr == NULL )
			sg_p_strid_mgr = new CStrIdMgr( );

		return sg_p_strid_mgr;
	}

	//=========================================================================

	bool_t CStrIdMgr::AddString( const char_t* apsz_s, hashid_t& ar_hash )
	{
		if( !SzCheck( apsz_s ) )
			return false;

		CMutexLocker locker( m_mutex );

		//Calculate the hash value of the string
		hashid_t h = FLOW_Hash_Function( apsz_s, strlen( apsz_s ), 0 );

		std::pair< TSzHashMap::iterator, bool_t > result;
		result = m_hm_strids.insert( std::make_pair( h, (CStrIdMgr::TStringHolder*)NULL ) );

		//Check the result of the insertion
		if( !result.second ) 
		{
			if( !(result.first->second)->Equals( apsz_s ) )
			{
				//! a collision in the 2^32 range occurred
				FLOW_ASSERT( false, "Hash collission detected!!: %s, %s", apsz_s, result.first->second->c_str( ) );
				return false;
			}
			//The string already existed
		}
		else
		{
			result.first->second = new TStringHolder( );
			result.first->second->Set( apsz_s );
			result.first->second->AddRef( );
		}

		ar_hash = h;

		return true;
	}

	//=========================================================================

	bool_t CStrIdMgr::AddString( hashid_t& ar_hash )
	{
		CMutexLocker locker( m_mutex );

		TSzHashMap::iterator it = m_hm_strids.find( ar_hash );
		if( it == m_hm_strids.end( ) )
			return false;

		it->second->AddRef( );
		return true;
	}

	//=========================================================================

	u32_t CStrIdMgr::ReleaseString( hashid_t a_h )
	{
		TSzHashMap::iterator it = m_hm_strids.find( a_h );
		if( it == m_hm_strids.end( ) )
			return 0; //String not found

		it->second->DecRef( );
		u32_t ref_count = it->second->GetRefCount( );
		if( ref_count == 0 )
		{
			CMutexLocker locker( m_mutex );
			SAFE_DELETE( it->second );
			m_hm_strids.erase( it );
		}
		
		return ref_count;
	}

	//=========================================================================

	const char_t* CStrIdMgr::GetString( hashid_t a_h )
	{
		CMutexLocker locker( m_mutex );

		TSzHashMap::iterator it = m_hm_strids.find( a_h );
		if( it != m_hm_strids.end( ) )
			return it->second->c_str( );
		else
			return NULL;
	}

} //namespace CORE